package view;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Observable;
import java.util.Random;
import java.util.Stack;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;


public class Game2048View extends Observable implements View,Runnable {
	
	private final int N=4;
	private final int winningScore=1024;
	private Display display;
	private Shell shell;
	private Board board;
	private Label label;
	private int[][] game2048;
	private int command;
	private int[] score;
	private MessageBox messageBox;
	private Stack<int[][]> game2048Stack = new Stack<int[][]>();
	private Stack<Integer> scoreStack = new Stack<Integer>();
	
	
	public void setReference(int[][] game2048,int[] score) {
		this.game2048 = game2048;
		this.score=score;
	}

	public void initgui(){
			
		//display
		display = new Display();
		Color blue = display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
		display.addFilter(SWT.KeyUp, new Listener()
	    {
	        public void handleEvent(Event e)
	        {
	                if (e.keyCode == SWT.ARROW_UP)
	                {
	                    command=1;
	                    setChanged();
	                    notifyObservers();
	                }
	                else if(e.keyCode == SWT.ARROW_DOWN)
	                {
	                    command=2;
	                    setChanged();
	                    notifyObservers();
	                }
	                else if(e.keyCode == SWT.ARROW_RIGHT)
	                {
	                    command=3;
	                    setChanged();
	                    notifyObservers();
	                }
	                else if(e.keyCode == SWT.ARROW_LEFT)
	                {
	                    command=4;
	                    setChanged();
	                    notifyObservers();
	                }
	            
	        }
	    });
		
		
		//shell
		shell = new Shell(display);
        shell.setLayout(new GridLayout(2,false));
		shell.setSize(400,300);
		shell.setText("2048 Game");
		shell.setBackground(blue);
		
		//score
		label = new Label(shell, SWT.NONE);
		label.setText("  Score: 0      ");
		label.setBackground(blue);

		//board
		board = new Board(shell,SWT.BORDER);
		board.setLayoutData(new GridData(SWT.FILL, SWT.FILL,true,true,1,5));
		board.setBoardData(game2048);
		board.updateTiles();
		
		
		//Menu
		Menu menuBar = new Menu(shell, SWT.BAR);
		MenuItem fileMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
		fileMenuHeader.setText("&File");
		Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
		fileMenuHeader.setMenu(fileMenu);
		final MenuItem fileSaveItem = new MenuItem(fileMenu, SWT.PUSH);
		fileSaveItem.setText("&Save");
		final MenuItem fileLoadItem = new MenuItem(fileMenu, SWT.PUSH);
		fileLoadItem.setText("&Load");
		final MenuItem fileExitItem = new MenuItem(fileMenu, SWT.PUSH);
		fileExitItem.setText("&Exit");
		MenuItem editMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
		editMenuHeader.setText("&Edit");
		Menu editMenu = new Menu(shell, SWT.DROP_DOWN);
		editMenuHeader.setMenu(editMenu);
		final MenuItem editUndoItem = new MenuItem(editMenu, SWT.PUSH);
		editUndoItem.setText("&Undo");
		final MenuItem editRestartItem = new MenuItem(editMenu, SWT.PUSH);
		editRestartItem.setText("&Restart");
		shell.setMenuBar(menuBar);
		
		//Menu listener 
		class MenuListener implements SelectionListener {
		    public void widgetSelected(SelectionEvent event) {
		    	if(event.widget==fileSaveItem){
		    		saveGame();
		    	}
		    	if(event.widget==fileLoadItem)
		    		loadGame();
		    	if(event.widget==fileExitItem)
		    		System.exit(0);
		    	if(event.widget==editUndoItem)
		    		undo();
		    	if(event.widget==editRestartItem)
		    		restartGame();
		    }
			public void widgetDefaultSelected(SelectionEvent e) {
			}
		}
		fileSaveItem.addSelectionListener(new MenuListener());
	    fileLoadItem.addSelectionListener(new MenuListener());
	    fileExitItem.addSelectionListener(new MenuListener());
	    editUndoItem.addSelectionListener(new MenuListener());
	    editRestartItem.addSelectionListener(new MenuListener());
	    

				
				
		//Buttons
		final Button undo = new Button(shell, SWT.PUSH);
		undo.setText("Undo Move");
		undo.setLayoutData(new GridData(SWT.FILL, SWT.TOP,false,false,1,1));
		undo.setBackground(blue);
		final Button restart = new Button(shell, SWT.PUSH);
		restart.setText("Restart Game");
		restart.setLayoutData(new GridData(SWT.FILL, SWT.TOP,false,false,1,1));
		restart.setBackground(blue);
		final Button load = new Button(shell, SWT.PUSH);
		load.setText("Load Game");
		load.setLayoutData(new GridData(SWT.FILL, SWT.TOP,false,false,1,1));
		load.setBackground(blue);
		final Button save = new Button(shell, SWT.PUSH);
		save.setText("Save Game");
		save.setLayoutData(new GridData(SWT.FILL, SWT.TOP,false,false,1,1));
		save.setBackground(blue);

		
		
		//Buttons listener
		Listener listener = new Listener() {
			public void handleEvent(Event event) {
				if(event.widget==undo)
					undo();
				if(event.widget==restart)	
					restartGame();
				if(event.widget==load)
				    loadGame();
				if(event.widget==save)
				    saveGame();
			}
			
		};
		undo.addListener(SWT.Selection, listener);
		restart.addListener(SWT.Selection, listener);
		load.addListener(SWT.Selection, listener);
		save.addListener(SWT.Selection, listener);	
		saveStacks();
		shell.open();
	}
	
	public void displayData() {
		board.updateTiles();
		label.setText("  Score: " + score[0]);
		saveStacks();
		checkBoard();
	}

	public int getUserCommand() {
		return command;
	}


	public void run() {
		initgui();
		while (!shell.isDisposed()){
			if (!display.readAndDispatch()){
				display.sleep();
			}
		}
		display.dispose();
		
	}
	
	public void restartGame() {
		for (int i=0;i<N;i++){
			for (int j=0;j<N;j++){
				game2048[i][j] =0;
			}
		}
		int count = 0;
		while (count < 2){
			Random rn = new Random();
			int random1 = rn.nextInt(N) + 0;
			int random2 = rn.nextInt(N) + 0;
			if (game2048[random1][random2] == 0){
				game2048[random1][random2] = 2;
				count++;
			}
			
		}
		score[0]=0;
		board.updateTiles();
		label.setText("  Score: 0");
		scoreStack.clear();
		game2048Stack.clear();
		saveStacks();
	}
	
	@SuppressWarnings("unchecked")
	public void loadGame() {
		try{
			FileDialog fd=new FileDialog(shell,SWT.OPEN);
			fd.setText("Load Game");
			fd.setFilterPath("C:/");
			String[] filterExt = { "*.txt", "*.java", ".xml", "*.*" };
			fd.setFilterExtensions(filterExt);
			String selected = fd.open();
			File file = new File(selected);
			int[][] localgame2048 = new int [N][N];
			int[] localscore = new int[1];
			FileInputStream fileStream = new FileInputStream(file);   
			ObjectInputStream objectStream = new ObjectInputStream(fileStream);   
			localgame2048 = (int[][]) objectStream.readObject();
			localscore = (int[]) objectStream.readObject();
			game2048Stack = (Stack<int[][]>) objectStream.readObject();
			scoreStack = (Stack<Integer>) objectStream.readObject();
					
			for (int i=0;i<N;i++){
				for (int j=0;j<N;j++){
					game2048[i][j] = localgame2048[i][j];
				}
			}	
			score[0] = localscore[0];
			
			objectStream.close();   
			fileStream.close();
			displayData();
		}
		catch  (Exception e) {   
		}
		
	}
	
	public void saveGame() {
		try{
			FileDialog fd=new FileDialog(shell,SWT.SAVE);
			fd.setText("Save Game");
			fd.setFilterPath("C:/");
			String[] filterExt = { "*.txt", "*.java", ".xml", "*.*" };
			fd.setFilterExtensions(filterExt);
			String selected = fd.open();
			File file = new File(selected);
			FileOutputStream fileStream = new FileOutputStream(file);   
			ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);    
			objectStream.writeObject(game2048);   
			objectStream.writeObject(score);
			objectStream.writeObject(game2048Stack);   
			objectStream.writeObject(scoreStack); 
			objectStream.close();   
			fileStream.close();  
		}
		catch  (Exception e) {   
		}
	}
	
	public void undo() {
		if (!scoreStack.empty() && !game2048Stack.empty()){ //avoid the first result of the same board status 
			scoreStack.pop();
			game2048Stack.pop();
		}
		if (!scoreStack.empty() && !game2048Stack.empty())
		{
			int[][] localgame2048 = new int[N][N];
			score[0] = scoreStack.peek();
			localgame2048 = game2048Stack.peek();
		
			for (int i=0;i<N;i++){
				for (int j=0;j<N;j++){
					game2048[i][j] = localgame2048[i][j] ;
				}
			}
			board.updateTiles();
			label.setText("  Score: " + score[0]);
		}	
	}
	
	public void saveStacks() {
		scoreStack.push (score[0]);
		int[][] localgame2048 = new int[N][N];
		for (int i=0;i<N;i++){
			for (int j=0;j<N;j++){
				localgame2048[i][j] = game2048[i][j];
			}
		}
		game2048Stack.push(localgame2048);
	}
	 

	
	public void checkBoard() {
		int count = 0;
		Boolean flag = false;
		for (int i=0;i<N;i++){
			for (int j=0;j<N;j++){
				if (game2048[i][j] != 0)
					count++;
				if (game2048[i][j] == 2048 || game2048[i][j] == winningScore)
					flag = true;
			}
		}
		if (flag == true){
			messageBox = new MessageBox(shell,  SWT.ICON_QUESTION| SWT.YES | SWT.NO);
			messageBox.setText("Congratulations");
			messageBox.setMessage("You Won! Do you want to play again?");
			int response = messageBox.open();
			if (response == SWT.NO)
				System.exit(0);
			if (response == SWT.YES)
				restartGame();
		}
		else if (count == N*N && !hasChanceToContinue()){
			messageBox = new MessageBox(shell,  SWT.ICON_QUESTION| SWT.YES | SWT.NO);
			messageBox.setText("Game Over");
			messageBox.setMessage("You Lost! Do you want to play again?");
			int response = messageBox.open();
			if (response == SWT.NO)
				System.exit(0);
			if (response == SWT.YES)
				restartGame();
		}
	}
	
	private boolean hasChanceToContinue() {
		for (int i=0;i<N;i++){
			for (int j=0;j<N;j++){
				if (i!=0 && i!=N-1 && j!=0 && j!=N-1 ){
					if (game2048[i][j] == game2048[i + 1][j] || game2048[i][j] == game2048[i - 1][j] || game2048[i][j] == game2048[i][j + 1] || game2048[i][j] == game2048[i][j - 1])
						return true;
				}
				else if (i==0 && j==0){
					if (game2048[i][j] == game2048[i + 1][j] || game2048[i][j] == game2048[i][j + 1])
						return true;
				}
				else if (i==N-1 && j==0){
					if (game2048[i][j] == game2048[i - 1][j] || game2048[i][j] == game2048[i][j + 1])
						return true;
				}
				else if (i==0 && j==N-1){
					if (game2048[i][j] == game2048[i + 1][j] || game2048[i][j] == game2048[i][j - 1])
						return true;
				}
				else if (i==N-1 && j==N-1){
					if (game2048[i][j] == game2048[i - 1][j] || game2048[i][j] == game2048[i][j - 1])
						return true;
				}
				else if (i==0){
					if (game2048[i][j] == game2048[i + 1][j] || game2048[i][j] == game2048[i][j - 1] || game2048[i][j] == game2048[i][j + 1])
						return true;
				}
				else if (i==N-1){
					if (game2048[i][j] == game2048[i - 1][j] || game2048[i][j] == game2048[i][j - 1] || game2048[i][j] == game2048[i][j + 1])
						return true;
				}
				else if (j==0){
					if (game2048[i][j] == game2048[i][j + 1] || game2048[i][j] == game2048[i + 1][j] || game2048[i][j] == game2048[i - 1][j])
						return true;
				}
				else if (j==N-1){
					if (game2048[i][j] == game2048[i][j - 1] || game2048[i][j] == game2048[i + 1][j] || game2048[i][j] == game2048[i - 1][j])
						return true;
				}
			}
		}
		return false;		
	}
}
